package com.ppch.zerocodegenerator.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.ppch.zerocodegenerator.ai.enums.CodeGenTypeEnum;
import com.ppch.zerocodegenerator.exception.ErrorCode;
import com.ppch.zerocodegenerator.exception.ThrowUtils;
import com.ppch.zerocodegenerator.mapper.AppMapper;
import com.ppch.zerocodegenerator.mapper.ChatHistoryMapper;
import com.ppch.zerocodegenerator.model.constant.UserConstant;
import com.ppch.zerocodegenerator.model.dto.chathistory.ChatHistoryQueryRequest;
import com.ppch.zerocodegenerator.model.entity.App;
import com.ppch.zerocodegenerator.model.entity.ChatHistory;
import com.ppch.zerocodegenerator.model.entity.User;
import com.ppch.zerocodegenerator.model.enums.ChatHistoryMessageTypeEnum;
import com.ppch.zerocodegenerator.service.ChatHistoryService;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.data.message.SystemMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;


/**
 * 对话历史 服务层实现。
 *
 * @author 泡泡茶壶
 * @since 2025-10-5
 */
@Service
@Slf4j
public class ChatHistoryServiceImpl extends ServiceImpl<ChatHistoryMapper, ChatHistory>  implements ChatHistoryService{

    @Resource
    private AppMapper appMapper;

    @Resource
    private ChatHistoryMapper chatHistoryMapper;

    /**
     * 新增对话历史
     * @param appId 应用ID
     * @param message 消息
     * @param messageType 消息类型
     * @param userId 用户ID
     * @return 是否成功
     */
    @Override
    public boolean addChatMessage(Long appId, String message, String messageType, Long userId) {
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR, "应用ID不能为空");
        ThrowUtils.throwIf(StrUtil.isBlank(message), ErrorCode.PARAMS_ERROR, "消息内容不能为空");
        ThrowUtils.throwIf(StrUtil.isBlank(messageType), ErrorCode.PARAMS_ERROR, "消息类型不能为空");
        ThrowUtils.throwIf(userId == null || userId <= 0, ErrorCode.PARAMS_ERROR, "用户ID不能为空");
        // 验证消息类型是否有效
        ChatHistoryMessageTypeEnum messageTypeEnum = ChatHistoryMessageTypeEnum.getEnumByValue(messageType);
        ThrowUtils.throwIf(messageTypeEnum == null, ErrorCode.PARAMS_ERROR, "不支持的消息类型: " + messageType);
        ChatHistory chatHistory = ChatHistory.builder()
                .appId(appId)
                .message(message)
                .messageType(messageType)
                .userId(userId)
                .build();
        return this.save(chatHistory);
    }


    /**
     * 将对话历史加载到Redis对话存储中
     * @param appId 应用ID
     * @param chatMemory 对话记忆
     * @param maxCount 最大加载条数
     * @return 加载成功的条数
     */
    @Override
    public int loadChatHistoryToMemory(Long appId, MessageWindowChatMemory chatMemory, int maxCount){
        try {
            // 构建查询条件
            QueryWrapper queryWrapper = QueryWrapper.create()
                    .select(ChatHistory::getMessageType,ChatHistory::getMessage)//只查询需要的字段而不是所有字段，提高查询效率
                    .from(ChatHistory.class)
                    .eq(ChatHistory::getAppId, appId, ObjUtil.isNotNull(appId))
                    .orderBy(ChatHistory::getCreateTime, false)
                    .limit(1,maxCount);
            // 获取对话历史列表
            List<ChatHistory> historyList = this.list(queryWrapper);
            if (CollUtil.isEmpty(historyList)) {
                return 0;
            }
            // 反转列表，确保按时间正序（老的在前，新的在后）
            Collections.reverse(historyList);
            // 清空MessageWindowChatMemory中的旧消息历史
            chatMemory.clear();
            // 按照时间顺序添加到chatMemory中
            int loadedCount = 0;
            // TODO：遍历 VUE 工程项目的对话历史，并去除消息类型为 AI 并且消息中包含"[工具调用]"的整行信息（包括行尾的换行符），解决 AI 假修改的 BUG。
            App app = appMapper.selectOneById(appId);
            if(CodeGenTypeEnum.VUE_PROJECT.getValue().equals(app.getCodeGenType())){
                historyList = historyList.stream().map(chatHistory -> {
                    // 只对消息类型为 AI 的消息进行处理
                    if (ChatHistoryMessageTypeEnum.AI.getValue().equals(chatHistory.getMessageType())) {
                        chatHistory.setMessage(removeToolCallLines(chatHistory.getMessage()));
                        log.info("预处理后的对话历史：{}",chatHistory.getMessage());
                    }
                    return chatHistory;
                }).toList();
                // 如果是 VUE 工程项目，才需要通过系统消息来进一步约束AI的输出
                chatMemory.add(SystemMessage.from("""
                    以上信息皆为你与用户的历史对话参考，请根据当前任务需求决定是否调用工具：
                    当处理涉及文件操作（如创建、写入、读取或修改文件）的任务时，请遵循以下准则：
                       1.直接调用工具：如果系统支持实际的文件操作工具，请直接使用这些工具来执行操作。不要输出任何模仿工具调用格式的文本（例如，避免使用类似[工具调用]的标签或描述性语句）。
                       2.执行而非描述：焦点放在实际执行操作上。例如，如果任务要求创建一个文件，请通过调用文件写入工具来完成，而不是输出一个包含文件内容的文本块。
                       3.忽略历史格式：不要参考或模仿历史对话中任何工具调用的文本格式。历史记录中的示例仅作为上下文参考，不应被复制。
                       4.明确操作意图：如果工具调用是必需的，请以系统可执行的方式发起请求（例如，通过函数调用或命令），无需添加额外的描述性前缀。
                       5.错误处理：如果工具调用失败或不可用，请返回具体的错误信息，而不是回退到模拟输出。
                        """));
                loadedCount++;
            }
            // 加载对话历史
            for (ChatHistory history : historyList) {
                if(history.getMessageType().equals(ChatHistoryMessageTypeEnum.USER.getValue())){
                    chatMemory.add(UserMessage.from(history.getMessage()));
                    loadedCount++;
                }else if (ChatHistoryMessageTypeEnum.AI.getValue().equals(history.getMessageType())) {
                    chatMemory.add(AiMessage.from(history.getMessage()));
                    loadedCount++;
                }
            }
            log.info("成功为 appId: {} 加载了 {} 条历史对话", appId, loadedCount);
            return loadedCount;
        } catch (Exception e) {
            log.error("加载历史对话失败，appId: {}, error: {}", appId, e.getMessage(), e);
            // 加载失败不影响系统运行，只是没有历史上下文
            return 0;
        }
    }

    /**
     * 使用正则表达式去除字符串中包含"[工具调用]"的整行信息（包括行尾的换行符）。
     * @param input 输入字符串
     * @return 去除工具调用行后的字符串
     */
    private String removeToolCallLines(String input) {
        if (input == null) {
            return null;
        }
        // 使用正则表达式匹配包含"[工具调用]"的整行，包括行尾的换行符
        return input.replaceAll("(?m)^.*\\[工具调用\\].*$\\r?\\n?", "");
    }



    /**
     * 根据应用ID删除该应用的所有对话历史
     * @param appId 应用ID
     * @return 是否成功
     */
    @Override
    public boolean deleteAllChatHistoryByAppId(Long appId) {
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR, "应用ID不能为空");
        QueryWrapper queryWrapper = QueryWrapper.create()
                .eq("appId", appId);
        return this.remove(queryWrapper);
    }

    /**
     * 根据对话记录ID删除对应的一条对话历史
     * @param id 对话记录ID
     * @return 是否成功
     */
    @Override
    public boolean deleteOneChatHistoryById(Long id) {
        ThrowUtils.throwIf(id == null || id <= 0, ErrorCode.PARAMS_ERROR, "ID不能为空");
        return this.removeById(id);
    }

    /**
     * 获取查询包装类
     *
     * @param chatHistoryQueryRequest 查询请求类
     * @return QueryWrapper
     */
    @Override
    public QueryWrapper getQueryWrapper(ChatHistoryQueryRequest chatHistoryQueryRequest) {
        QueryWrapper queryWrapper = QueryWrapper.create();
        if (chatHistoryQueryRequest == null) {
            return queryWrapper;
        }
        Long id = chatHistoryQueryRequest.getId();
        String message = chatHistoryQueryRequest.getMessage();
        String messageType = chatHistoryQueryRequest.getMessageType();
        Long appId = chatHistoryQueryRequest.getAppId();
        Long userId = chatHistoryQueryRequest.getUserId();
        LocalDateTime lastCreateTime = chatHistoryQueryRequest.getLastCreateTime();
        String sortField = chatHistoryQueryRequest.getSortField();
        String sortOrder = chatHistoryQueryRequest.getSortOrder();
        // 拼接查询条件
        queryWrapper.eq(ChatHistory::getId, id)
                .like(ChatHistory::getMessage, message)
                .eq(ChatHistory::getMessageType, messageType, StrUtil.isNotBlank(messageType))
                .eq(ChatHistory::getAppId, appId)
                .eq(ChatHistory::getUserId, userId);
        // 游标查询逻辑 - 只使用 createTime 作为游标
        if (lastCreateTime != null) {
            queryWrapper.lt(ChatHistory::getCreateTime, lastCreateTime);
        }
        // 排序
        if (StrUtil.isNotBlank(sortField)) {
            queryWrapper.orderBy(sortField, "ascend".equals(sortOrder));
        } else {
            // 默认按创建时间降序排列
            queryWrapper.orderBy(ChatHistory::getCreateTime, false);
        }
        return queryWrapper;
    }


    /**
     * 分页查询应用对话历史（所有人都可以查看）
     */
    @Override
    public Page<ChatHistory> listAppChatHistoryByPage(Long appId, int pageSize,
                                                      LocalDateTime lastCreateTime,
                                                      User loginUser) {
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR, "应用ID不能为空");
        ThrowUtils.throwIf(pageSize <= 0 || pageSize > 50, ErrorCode.PARAMS_ERROR, "页面大小必须在1-50之间");
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NOT_LOGIN_ERROR);
        // 验证权限：只有应用创建者和管理员可以查看
        App app = appMapper.selectOneById(appId);
        ThrowUtils.throwIf(app == null, ErrorCode.NOT_FOUND_ERROR, "应用不存在");
        // TODO：改为所有用户都可以查看
        /*boolean isAdmin = UserConstant.ADMIN_ROLE.equals(loginUser.getUserRole());
        boolean isCreator = app.getUserId().equals(loginUser.getId());
        ThrowUtils.throwIf(!isAdmin && !isCreator, ErrorCode.NO_AUTH_ERROR, "无权查看该应用的对话历史");*/
        // 构建查询条件
        ChatHistoryQueryRequest queryRequest = new ChatHistoryQueryRequest();
        queryRequest.setAppId(appId);
        queryRequest.setLastCreateTime(lastCreateTime);
        QueryWrapper queryWrapper = this.getQueryWrapper(queryRequest);
        // 查询数据
        return this.page(Page.of(1, pageSize), queryWrapper);
    }


}
